package com.aokang.footwear.common.exception;

import com.aokang.footwear.common.enums.ErrorCodeEnum;
import com.aokang.footwear.common.i18n.IErrorCodeEnum;
import com.aokang.footwear.common.i18n.ResultHelper;
import com.aokang.footwear.common.i18n.ResultI18n;
import com.chiyoyo.tokenhelper.LoginInfoDTO;
import com.chiyoyo.tokenhelper.TokenHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author chilei
 */
@Slf4j
@RestControllerAdvice
public class BaseExceptionHandler {

    @Resource
    private HttpServletRequest httpServletRequest;

    public void printErrorLog(String msg, Exception e) {
        String requestURI = httpServletRequest.getRequestURI();
        String token = httpServletRequest.getHeader("Authorization");

        LoginInfoDTO loginInfo = TokenHelper.getLoginInfo(token);
        log.error("requestUrl[{}] userid:[{}],{}: ", requestURI, loginInfo.getUserid(), msg, e);
    }

    /**
     * 接口404
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResultI18n<String> handlerNoFoundException(Exception e) {
        printErrorLog("path does not exist err ", e);
        return ResultHelper.fail(404);
    }

    /**
     * 数据库中已存在该记录
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public ResultI18n<String> handleDuplicateKeyException(DuplicateKeyException e) {
        printErrorLog("the database already exists err ", e);
        return ResultHelper.fail(ErrorCodeEnum.RECORD_ALREADY_EXISTS);
    }

    /**
     * 数据错误
     */
    @ExceptionHandler(DataAccessException.class)
    public ResultI18n<String> handleDuplicateKeyException(DataAccessException e) {
        printErrorLog("data access err ", e);
        return ResultHelper.fail(ErrorCodeEnum.DATA_ERROR);
    }

    @ExceptionHandler(Exception.class)
    public ResultI18n<?> handleException(Exception e) {
        printErrorLog("err ", e);
        return ResultHelper.fail(ErrorCodeEnum.UNKNOWN_ERROR);
    }

    @ExceptionHandler(value = BaseServiceException.class)
    public ResultI18n<?> baseServiceExceptionHandler(BaseServiceException exception) {
        //message ==> 国际化文件的key
        IErrorCodeEnum iErrorCodeEnum = exception.getIErrorCodeEnum();
        printErrorLog(exception.getMessage(), null);
        if (iErrorCodeEnum != null) {
            return ResultHelper.fail(iErrorCodeEnum, exception.getResponseData());
        } else {
            return ResultHelper.failResult(exception.getCode(), exception.getMessage(), exception.getResponseData());
        }
    }

    /**
     * 参数校验异常
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResultI18n<String> argumentExceptionHandler(MethodArgumentNotValidException exception) {
        // 从 FieldError 中获取字段名和默认的错误信息
        List<String> errors = exception.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(fieldError -> String.format("%s : %s, argument err", fieldError.getField(), fieldError.getRejectedValue()))
                .collect(Collectors.toList());
        //message ==> 国际化文件的key
        String message = Objects.requireNonNull(exception.getBindingResult().getFieldError()).getDefaultMessage();
        log.info("requestUrl[{}],err : {}", httpServletRequest.getRequestURI(), errors);
        printErrorLog(errors.toString(), null);
        if (message == null || message.isEmpty()) {
            return ResultHelper.fail(ErrorCodeEnum.UNKNOWN_ERROR);
        }
        ErrorCodeEnum errorCodeEnum = ErrorCodeEnum.getByCode(message.matches("\\d+") ? Integer.parseInt(message) : 500);
        return ResultHelper.fail(errorCodeEnum);
    }
}
